package com.mobilesalon.business;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import android.app.backup.BackupManager;
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;

import com.mobilesalon.calendar.Attendee;
import com.mobilesalon.calendar.CalendarEventBean;
import com.mobilesalon.calendar.CalendarProvider;
import com.mobilesalon.calendar.PhoneCalendarBean;
import com.mobilesalon.contact.Contact;
import com.mobilesalon.contact.ContactProvider;
import com.mobilesalon.model.AppointmentBean;
import com.mobilesalon.model.ServiceBean;
import com.mobilesalon.model.ServiceClientBean;
import com.mobilesalon.provider.DBHelper;
import com.mobilesalon.provider.ServiceClientContract.Appointment;
import com.mobilesalon.provider.ServiceClientContract.Service;
import com.mobilesalon.provider.ServiceClientContract.ServiceClient;
import com.mobilesalon.provider.Tables;
import com.mobilesalon.tools.DateTools;

public class ServiceClientManager {

	private static final String TAG = ServiceClientManager.class.getName();

	private final DBHelper dbHelper;
	private final CalendarProvider calendarProvider;
	private final ContactProvider contactProvider;
	private final PreferencesManager prefsManager;
	private final BackupManager backupManager;

	public ServiceClientManager(DBHelper dbHelper, CalendarProvider calendarProvider,
			ContactProvider contactProvider, PreferencesManager prefsManager,
			BackupManager backupManager) {
		this.dbHelper = dbHelper;
		this.calendarProvider = calendarProvider;
		this.contactProvider = contactProvider;
		this.prefsManager = prefsManager;
		this.backupManager = backupManager;
	}

	public ServiceClientBean add(ServiceClientBean serviceClient) {
		if (serviceClient.getServiceBean() == null) {
			throw new IllegalStateException("ServiceBean is missing");
		}

		ContentValues values = new ContentValues();
		values.put(ServiceClient.SERVICE_ID, serviceClient.getServiceBean().getServiceId());
		values.put(ServiceClient.CLIENT_LOOKUP_KEY, serviceClient.getClient().getLookupKey());
		values.put(ServiceClient.CLIENT_DISPLAY_NAME, serviceClient.getClient().getDisplayName());
		values.put(ServiceClient.PRICE, serviceClient.getPrice());

		String date = DateTools.TIMESTAMP_DATEFORMAT.format(serviceClient.getDate().getTime());
		values.put(ServiceClient.DATE, date);

		values.put(ServiceClient.APPOINTMENT_ID, serviceClient.getAppointmentId());

		long id = dbHelper.getDb().insert(Tables.SERVICE_CLIENT_TABLE, null, values);

		if (id != -1) {
			serviceClient.setId(id);
			// shedule backup
			backupManager.dataChanged();
			return serviceClient;
		} else {
			return null;
		}

	}

	public AppointmentBean add(AppointmentBean appointmentBean) {
		ContentValues values = new ContentValues();
		values.put(Appointment.CLIENT_DISPLAY_NAME, appointmentBean.getClientDisplayName());
		values.put(Appointment.CLIENT_LOOKUP_KEY, appointmentBean.getClientLookupKey());
		values.put(Appointment.DATE,
				DateTools.TIMESTAMP_DATEFORMAT.format(appointmentBean.getDate().getTime()));
		values.put(Appointment.CALENDAR_ID, appointmentBean.getCalendarId());
		values.put(Appointment.CALENDAR_OWNER, appointmentBean.getCalendarOwner());
		long id = dbHelper.getDb().insert(Tables.APPOINTMENT_TABLE, null, values);

		if (id != -1) {
			appointmentBean.setId(id);
			appointmentBean.setIsStored(true);
			return appointmentBean;
		} else {
			return null;
		}
	}

	/**
	 * Appointments are retrieved from calendars defined in SharedPreferences.
	 * 
	 * @return
	 */
	public List<AppointmentBean> fetchDailyAppointments(Calendar day) throws ConfigurationException {

		//
		// Setup Timeframe
		//
		final Calendar from = DateTools.roundByDay(day);
		final Calendar to = Calendar.getInstance();
		to.setTime(from.getTime());
		to.add(Calendar.DAY_OF_MONTH, 1);

		//
		// Fetch calendar entries
		//
		List<CalendarEventBean> calendarEvents = this.fetchCalendarEntries(from, to);

		//
		// Union of entries calendars and appointments
		//
		List<AppointmentBean> appointmentBeans = this.fetchStoredAppointments(from, to);

		for (CalendarEventBean calendarEvent : calendarEvents) {
			boolean isAlreadyStored = false;
			for (AppointmentBean appointmentBeanLocal : appointmentBeans) {
				if (appointmentBeanLocal.getDate().equals(calendarEvent.getBegin())) {
					isAlreadyStored = true;
					break;
				}
			}
			if (!isAlreadyStored) {
				// add calendareEvent
				AppointmentBean appointmentBean = new AppointmentBean();
				appointmentBean.setDate(calendarEvent.getBegin());

				List<Attendee> attendees = calendarEvent.getAttendees();
				if (attendees.size() != 0) {
					Attendee firstAttendee = attendees.get(0);
					Contact contact = contactProvider.getContactByEmailAdress(firstAttendee
							.getAttendeeEmail());

					appointmentBean.setClientDisplayName(contact.getDisplayName());
					appointmentBean.setClientLookupKey(contact.getLookupKey());

				} else {
					appointmentBean.setClientDisplayName(calendarEvent.getTitle());
				}
				appointmentBean.setIsStored(false);
				appointmentBean.setCalendarId(calendarEvent.getPhoneCalendar().getId());
				appointmentBean
						.setCalendarOwner(calendarEvent.getPhoneCalendar().getAccountOwner());
				appointmentBeans.add(appointmentBean);

			}

		}

		//
		// Sort appointments by startTime
		//

		Collections.sort(appointmentBeans, new Comparator<AppointmentBean>() {

			public int compare(AppointmentBean object1, AppointmentBean object2) {
				return (int) (object1.getDate().getTimeInMillis() - object2.getDate()
						.getTimeInMillis());
			}

		});
		return appointmentBeans;
	}

	/**
	 * 
	 * @param fromCalendar
	 * @param toCalendar
	 * @return calendar entries with respect to configured calendars
	 * @throws ConfigurationException
	 */
	private List<CalendarEventBean> fetchCalendarEntries(Calendar fromCalendar, Calendar toCalendar)
			throws ConfigurationException {
		//
		// Retrieve Ids of configured calendars
		//
		List<String> calendarAccountOwners = prefsManager.getSelectedCalendarsOwners();

		//
		// For each calendars, fetchEntries
		//
		final List<CalendarEventBean> calendarEvents = new LinkedList<CalendarEventBean>();
		for (String calendarAccountOwner : calendarAccountOwners) {

			PhoneCalendarBean calendar = calendarProvider
					.getPhoneCalendarsByAccountOwner(calendarAccountOwner);

			List<CalendarEventBean> calendarEventLocal = calendarProvider.getCalendarEvents(
					calendar, fromCalendar, toCalendar);
			calendarEvents.addAll(calendarEventLocal);
		}

		//
		// Filter calendar entries
		//
		Iterator<CalendarEventBean> iterator = calendarEvents.iterator();
		while (iterator.hasNext()) {
			CalendarEventBean local = iterator.next();
			if (local.getAllDay()) {
				iterator.remove();
			}
		}

		return calendarEvents;

	}

	/**
	 * 
	 * @param fromCalendar
	 * @param toCalendar
	 * @return appointments with respect to configured calendars
	 * @throws ConfigurationException
	 */
	public List<AppointmentBean> fetchStoredAppointments(Calendar fromCalendar, Calendar toCalendar)
			throws ConfigurationException {
		final String fromString = DateTools.DATE_REVERSE_DATEFORMAT.format(fromCalendar.getTime());
		final String toString = DateTools.DATE_REVERSE_DATEFORMAT.format(toCalendar.getTime());

		Cursor appointmentCursor = dbHelper.fetchAppointmentsBetweenDates(fromString, toString);

		List<AppointmentBean> appointmentBeans = new ArrayList<AppointmentBean>(
				appointmentCursor.getCount());

		final List<String> configuredCalendarsOwners = prefsManager.getSelectedCalendarsOwners();
		while (appointmentCursor.moveToNext()) {
			String calendarOwner = appointmentCursor.getString(appointmentCursor
					.getColumnIndex(Appointment.CALENDAR_OWNER));
			// skip entries from non conigured calendars
			if (!configuredCalendarsOwners.contains(calendarOwner)) {
				continue;
			}

			long appointmentId = appointmentCursor.getLong(appointmentCursor
					.getColumnIndex(Appointment._ID));

			String clientLookupKey = appointmentCursor.getString(appointmentCursor
					.getColumnIndex(Appointment.CLIENT_LOOKUP_KEY));

			String clientDisplayName = appointmentCursor.getString(appointmentCursor
					.getColumnIndex(Appointment.CLIENT_DISPLAY_NAME));

			long calendarId = appointmentCursor.getLong(appointmentCursor
					.getColumnIndex(Appointment.CALENDAR_ID));

			String dateString = appointmentCursor.getString(appointmentCursor
					.getColumnIndex(Appointment.DATE));
			Calendar date = Calendar.getInstance();

			try {
				date.setTime(DateTools.TIMESTAMP_DATEFORMAT.parse(dateString));
			} catch (ParseException e) {
				Log.e(TAG, "Cannot parse date");
				e.printStackTrace();
			}

			String location = appointmentCursor.getString(appointmentCursor
					.getColumnIndex(Appointment.LOCATION));

			float price;

			price = appointmentCursor.getFloat(appointmentCursor.getColumnIndex("sum"));
			AppointmentBean appointmentBean = new AppointmentBean(appointmentId, clientLookupKey,
					clientDisplayName, calendarId, calendarOwner, date, location, true, price);

			appointmentBeans.add(appointmentBean);
		}
		return appointmentBeans;

	}

	public AppointmentBean fetchAppointmentById(long appointmentId) {
		Cursor cursor = dbHelper.fetchAppointmentById(appointmentId);
		if (cursor == null || !cursor.moveToFirst()) {
			return null;
		}

		String clientLookupKey = cursor.getString(cursor
				.getColumnIndex(Appointment.CLIENT_LOOKUP_KEY));

		String clientDisplayName = cursor.getString(cursor
				.getColumnIndex(Appointment.CLIENT_DISPLAY_NAME));

		long calendarId = cursor.getLong(cursor.getColumnIndex(Appointment.CALENDAR_ID));

		String calendarOwner = cursor.getString(cursor.getColumnIndex(Appointment.CALENDAR_OWNER));

		String dateString = cursor.getString(cursor.getColumnIndex(Appointment.DATE));
		Calendar date = Calendar.getInstance();
		try {
			date.setTime(DateTools.TIMESTAMP_DATEFORMAT.parse(dateString));
		} catch (ParseException e) {
			Log.e(TAG, "Cannot parse date");
			e.printStackTrace();
		}

		String location = cursor.getString(cursor.getColumnIndex(Appointment.LOCATION));

		// fetch price
		float price;
		Cursor priceCursor = dbHelper.fetchAppointmentPriceById(appointmentId);
		if (priceCursor == null || !priceCursor.moveToFirst()) {
			price = 0;
		}

		price = priceCursor.getFloat(priceCursor.getColumnIndex("price"));
		AppointmentBean appointmentBean = new AppointmentBean(appointmentId, clientLookupKey,
				clientDisplayName, calendarId, calendarOwner, date, location, true, price);
		return appointmentBean;
	}

	public void deleteServiceClientById(long serviceClientId) {
		dbHelper.deleteServiceClient(serviceClientId);
	}

	public Cursor fetchServiceClientsByAppointmentId(long appointmentId) {
		return dbHelper.fetchServiceClientsByAppointmentId(appointmentId);
	}

	public List<ServiceBean> fetchServices() {
		List<ServiceBean> list = Collections.emptyList();

		Cursor cursor = dbHelper.fetchServices();
		if (cursor == null || !cursor.moveToFirst()) {
			return null;
		}

		list = new ArrayList<ServiceBean>(cursor.getCount());

		while (!cursor.isAfterLast()) {
			long serviceId = cursor.getLong(cursor.getColumnIndex(Service._ID));
			String serviceName = cursor.getString(cursor.getColumnIndex(Service.SERVICE_NAME));
			long price = cursor.getLong(cursor.getColumnIndex(Service.DEFAULT_PRICE));

			ServiceBean servicebean = new ServiceBean(serviceId, price, serviceName);
			list.add(servicebean);
			cursor.moveToNext();

		}

		return list;

	}
}
